package com.example.manager;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.example.bean.AISPosition;
import com.example.bean.AISStatic;
import com.example.constant.CommonConst;
import com.example.constant.TopicConst;
import com.example.utils.GeoUtils;
import com.example.utils.MqttUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.marineapi.ais.message.*;
import net.sf.marineapi.ais.parser.AISMessageFactory;
import net.sf.marineapi.nmea.parser.SentenceFactory;
import net.sf.marineapi.nmea.sentence.AISSentence;
import net.sf.marineapi.nmea.sentence.Sentence;

import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;

/**
 * AIS管理器
 *
 * @author: Fred
 * @email 453086@qq.com
 * @create: 2024-09-29 14:23
 */
@Slf4j
public class AisManager {

    private static final AisManager instance = new AisManager();

    private final Map<Integer, AISPosition> vesselPositionMap = new ConcurrentHashMap<>();

    private final Map<String, AISSentence[]> partialMessages = new ConcurrentHashMap<>();
    private final Set<String> receivedMessageSet = ConcurrentHashMap.newKeySet();
    private final Map<String, Long> messageTimestamps = new ConcurrentHashMap<>();
    private final long TIMEOUT_MS = 30000;


    private AisManager() {
        startCleanupTask();
    }

    public static AisManager getInstance() {
        return instance;
    }

    public void putVesselPosition(AISPosition position) {
        vesselPositionMap.put(position.getMmsi(), position);
    }

    public AISPosition getVesselPosition(Integer mmsi) {
        return vesselPositionMap.get(mmsi);
    }


    /**
     * 解析单条AIS消息
     *
     * @param message AIS消息字符串
     */
    public void parseAISMessage(String message) {
        try {
            SentenceFactory factory = SentenceFactory.getInstance();
            Sentence sentence = factory.createParser(message.trim());

            if (sentence instanceof AISSentence) {
                AISSentence aisSentence = (AISSentence) sentence;
                if (aisSentence.getNumberOfFragments() == 1) {
                    // 单句消息，直接解析
                    processCompletedMessage(new AISSentence[]{aisSentence});
                } else {
                    // 多句消息，需要合并
                    handleMultiPartMessage(aisSentence);
                }
            }
        } catch (Exception e) {
            log.error("Error parsing AIS message: " + e.getMessage());
        }
    }

    /**
     * 处理完整的AIS消息
     *
     * @param sentences 完整的AISSentence数组
     */
    private void processCompletedMessage(AISSentence[] sentences) {
        try {
            AISMessage aisMessage = AISMessageFactory.getInstance().create(sentences);
            switch (aisMessage.getMessageType()) {
                case 1:
                case 2:
                case 3:
                    parseClassAPositionReport(aisMessage);
                    break;
                case 5:
                    parseClassAStaticData(aisMessage);
                    break;
                case 18:
                case 19:
                    parseClassBPositionReport(aisMessage);
                    break;
                case 20:
                    break;
                case 24:
                    parseClassBStaticData(aisMessage);
                    break;
                default:
                    log.warn("不支持的消息类型: " + aisMessage.getMessageType());
            }
        } catch (Exception e) {
            log.error("处理完整AIS消息时出错: " + e.getMessage());
        }
    }

    /**
     * 处理多部分AIS消息
     *
     * @param aisSentence AIS句子
     */
    private void handleMultiPartMessage(AISSentence aisSentence) {
        String messageId = aisSentence.getMessageId();
        if (aisSentence.getFragmentNumber() < 1 ||
                aisSentence.getFragmentNumber() > aisSentence.getNumberOfFragments()) {
            throw new IllegalArgumentException("片段编号和总数异常！");
        }
        if (!partialMessages.containsKey(messageId)) {
            partialMessages.put(messageId, new AISSentence[aisSentence.getNumberOfFragments()]);
        }


        AISSentence[] fragments = partialMessages.get(messageId);
        synchronized (fragments) {
            fragments[aisSentence.getFragmentNumber() - 1] = aisSentence;
            // 更新消息的时间戳
            messageTimestamps.put(messageId, System.currentTimeMillis());
            if (Arrays.stream(fragments).allMatch(Objects::nonNull)) {
                processCompletedMessage(fragments);
                partialMessages.remove(messageId);
            }
        }
    }


    /**
     * 解析位置报告（消息类型1、2、3）
     *
     * @param aisMessage AIS消息对象
     */
    private void parseClassAPositionReport(AISMessage aisMessage) {
        AISPositionReport posReport = (AISPositionReport) aisMessage;

        if (!isInBoundaryAndMoving(posReport.getLongitudeInDegrees(), posReport.getLatitudeInDegrees(), posReport.getSpeedOverGround())) {
            return;
        }
        String timeStr = formatAISMessageTime(posReport.getTimeStamp());
        if (isDuplicateMessage(posReport.getMMSI(), timeStr)) {
            return;
        }
        AISPosition aisPosition = new AISPosition();
        aisPosition.setMmsi(posReport.getMMSI());
        aisPosition.setLongitude(posReport.getLongitudeInDegrees());
        aisPosition.setLatitude(posReport.getLatitudeInDegrees());
        aisPosition.setSpeed(posReport.getSpeedOverGround());
        aisPosition.setCourse(calculateCourse(aisPosition, posReport.getCourseOverGround()));
        if (aisPosition.getCourse() <= 0 || aisPosition.getCourse() >= 360) {
            return;
        }
        aisPosition.setHeading(posReport.getTrueHeading());
        aisPosition.setRot(posReport.getRateOfTurn());
        aisPosition.setNavStatus(posReport.getNavigationalStatus());
        aisPosition.setDataTime(timeStr);

        log.info("收到ClassA数据：" + JSONUtil.toJsonStr(aisPosition));


        putVesselPosition(aisPosition);
        MqttUtil.mqttTemplate.publishAsync(TopicConst.AIS_POINT + aisPosition.getMmsi(), JSONUtil.toJsonStr(aisPosition).getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 解析静态和航程数据（消息类型5）
     *
     * @param aisMessage AIS消息对象
     */
    private void parseClassAStaticData(AISMessage aisMessage) {
        AISMessage05 staticData = (AISMessage05) aisMessage;

        LocalDateTime updateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");

        if (isDuplicateMessage(staticData.getMMSI(), updateTime.format(formatter))) {
            return;
        }

        AISStatic aisStatic = new AISStatic();
        aisStatic.setMmsi(staticData.getMMSI());
        aisStatic.setImo(staticData.getIMONumber());
        Optional.ofNullable(staticData.getCallSign()).ifPresent(callSign -> aisStatic.setCallSign(callSign.replaceAll("@", "")));
        aisStatic.setVesselName(staticData.getName());
        aisStatic.setVesselType(staticData.getTypeOfShipAndCargoType());
        aisStatic.setToBow(staticData.getBow());
        aisStatic.setToStern(staticData.getStern());
        aisStatic.setToPort(staticData.getPort());
        aisStatic.setToStarboard(staticData.getStarboard());
        // 判断是否年和日都大于0
        if (staticData.getETAMonth() > 0 && staticData.getETADay() > 0) {
            aisStatic.setEta(formatETA(staticData.getETAMonth(), staticData.getETADay(), staticData.getETAHour(), staticData.getETAMinute()));
        }
        aisStatic.setDraft(staticData.getMaximumDraught());
        Optional.ofNullable(staticData.getDestination()).ifPresent(destination -> aisStatic.setDestination(destination.replaceAll("@", "")));

        MqttUtil.mqttTemplate.publishAsync("/ais/static/secret", JSONUtil.toJsonStr(aisStatic).getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 解析Class B设备位置报告（消息类型18）
     *
     * @param aisMessage AIS消息对象
     */
    private void parseClassBPositionReport(AISMessage aisMessage) {
        AISPositionReportB classBPos = (AISPositionReportB) aisMessage;

        // 检查经纬度是否在边界内且速度不能小于0.5
        if (!isInBoundaryAndMoving(classBPos.getLongitudeInDegrees(), classBPos.getLatitudeInDegrees(), classBPos.getSpeedOverGround())) {
            return;
        }

        int timestamp = classBPos.getTimeStamp();
        String timeStr = formatAISMessageTime(timestamp);
        if (isDuplicateMessage(classBPos.getMMSI(), timeStr)) {
            return;
        }

        AISPosition aisPosition = new AISPosition();
        aisPosition.setMmsi(classBPos.getMMSI());
        aisPosition.setLongitude(classBPos.getLongitudeInDegrees());
        aisPosition.setLatitude(classBPos.getLatitudeInDegrees());
        aisPosition.setSpeed(classBPos.getSpeedOverGround());
        aisPosition.setCourse(calculateCourse(aisPosition, classBPos.getCourseOverGround()));
        if (aisPosition.getCourse() <= 0 || classBPos.getCourseOverGround() >= 360) {
            return;
        }
        aisPosition.setHeading(classBPos.getTrueHeading());
        aisPosition.setDataTime(timeStr);

        log.info("收到ClassB数据：" + JSONUtil.toJsonStr(aisPosition));
        putVesselPosition(aisPosition);
        MqttUtil.mqttTemplate.publishAsync(TopicConst.AIS_POINT + aisPosition.getMmsi(), JSONUtil.toJsonStr(aisPosition).getBytes(StandardCharsets.UTF_8));
    }

    private void parseClassBStaticData(AISMessage aisMessage) {
        AISMessage24 classBStatic = (AISMessage24) aisMessage;

        LocalDateTime updateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH");

        if (isDuplicateMessage(classBStatic.getMMSI(), updateTime.format(formatter))) {
            return;
        }
        AISStatic aisStatic = new AISStatic();
        aisStatic.setMmsi(classBStatic.getMMSI());
        aisStatic.setVesselName(classBStatic.getName());
        aisStatic.setVesselType(classBStatic.getTypeOfShipAndCargoType());
        Optional.ofNullable(classBStatic.getCallSign()).ifPresent(callSign -> aisStatic.setCallSign(callSign.replaceAll("@", "")));
        aisStatic.setToBow(classBStatic.getBow());
        aisStatic.setToStern(classBStatic.getStern());
        aisStatic.setToPort(classBStatic.getPort());
        aisStatic.setToStarboard(classBStatic.getStarboard());

        MqttUtil.mqttTemplate.publishAsync("/ais/static/secret", JSONUtil.toJsonStr(aisStatic).getBytes(StandardCharsets.UTF_8));
    }

    private boolean isDuplicateMessage(int mmsi, String messageContent) {
        if (mmsi > 999999000) {
            return true;
        }
        String hash = SecureUtil.sha256(mmsi + messageContent);
        if (receivedMessageSet.contains(hash)) {
            return true;
        } else {
            if (receivedMessageSet.size() >= 1000) {
                receivedMessageSet.clear();
            }
            receivedMessageSet.add(hash);
            return false;
        }
    }


    private boolean isInBoundaryAndMoving(double longitude, double latitude, double speed) {
        return longitude >= CommonConst.MIN_LONGITUDE && longitude <= CommonConst.MAX_LONGITUDE
                && latitude >= CommonConst.MIN_LATITUDE && latitude <= CommonConst.MAX_LATITUDE
                && speed >= CommonConst.MIN_SPEED;
    }

    /**
     * 结合当前时间和 AIS 消息中的 TimeStamp 格式化时间
     *
     * @param timeStamp AIS 消息中的时间戳
     * @return 格式化后的时间字符串
     */
    private String formatAISMessageTime(int timeStamp) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime aisTime;

        // 判断时间戳的有效性
        if (timeStamp < 0 || timeStamp >= 60) {
            return now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        if (timeStamp > now.getSecond()) {
            // 如果 TimeStamp 大于当前秒数，说明消息来自于上一分钟
            aisTime = now.minusMinutes(1).withSecond(timeStamp);
        } else {
            // 否则，直接替换当前秒数
            aisTime = now.withSecond(timeStamp);
        }
        // 返回格式化后的时间
        return aisTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    private double calculateCourse(AISPosition aisPosition, double course) {
        if (course <= 0 || course >= 360) {
            AISPosition previousPosition = getVesselPosition(aisPosition.getMmsi());
            if (previousPosition != null) {
                course = GeoUtils.calculateBearing(previousPosition.getLatitude(), previousPosition.getLongitude(), aisPosition.getLatitude(), aisPosition.getLongitude());
            }
        }
        return course;
    }

    private String formatETA(int month, int day, int hour, int minute) {
        // 使用更清晰的变量名，并简化逻辑
        try {
            LocalDateTime now = LocalDateTime.now();
            int currentYear = now.getYear();
            int etaYear = (month < now.getMonthValue()) ? currentYear + 1 : currentYear;
            // 创建LocalDateTime对象，并检查日期有效性
            LocalDateTime etaTime = LocalDateTime.of(etaYear, month, day, hour, minute);
            return etaTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        } catch (DateTimeException e) {
            log.error("Invalid date or time: " + e.getMessage());
        }
        return "";
    }

    // 定期清理超时的未完成消息
    private void startCleanupTask() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> {
            long currentTime = System.currentTimeMillis();
            messageTimestamps.forEach((messageId, timestamp) -> {
                if (currentTime - timestamp > TIMEOUT_MS) {
                    partialMessages.remove(messageId);
                    messageTimestamps.remove(messageId);
                }
            });
        }, 10, 10, TimeUnit.SECONDS);
    }
}